package userws

import (
	"config"
	"encoding/json"
	"fmt"
	"github.com/anachronistic/apns"
	"github.com/garyburd/redigo/redis"
	"github.com/gorilla/websocket"
	"log"
	"net/http"
	"rhelper"
	"strconv"
	"time"
)

var (
//ws *websocket.Conn
//uniqueId string
//cid string
)

type websocketHolder struct {
	ws  *websocket.Conn
	cid string
}

var (
	upgrader = websocket.Upgrader{ReadBufferSize: 1024,
		WriteBufferSize: 1024}
	WebsockRepos = make(map[string]*websocketHolder)
)

func AcceptWebsocket_purifiers(w http.ResponseWriter, r *http.Request) {
	var (
		ws       *websocket.Conn
		uniqueId string
		cid      string
	)

	uniqueId = r.Header.Get("uniqueId")
	cid = r.Header.Get("cid")

	//rhelper.UpdateFlag["85133f6b-a0ec-4a47-a8a7-7779f6e91ba1"] = make(chan int, 20)
	userStat := make(chan int, 1)

	rhelper.Subscriptions[cid] = append(rhelper.Subscriptions[cid], userStat)

	if uniqueId == "" || cid == "" {
		log.Println("nil account and cid")
		return
	}

	log.Println("accept a new user websocket, account:", uniqueId)

	ws, _ = upgrader.Upgrade(w, r, nil)

	older, ok := WebsockRepos[uniqueId]
	if ok {
		//sendMessageToOldWS(account)
		log.Println("duplicate websocket, close old one")
		older.ws.Close()
		//<-older.chanx
	}

	WebsockRepos[uniqueId] = &websocketHolder{ws, cid}

	go reportTicker(ws, uniqueId, cid, userStat)
}

func reportTicker(ws *websocket.Conn, uniqueId string, cid string, userStat chan int) {
	ticker := time.NewTicker(5 * time.Second)
	quit := make(chan int)
	for {
		select {
		case <-userStat:
			reportRuntimeInfo(quit, ws, uniqueId, cid)
		case <-ticker.C:
			reportRuntimeInfo(quit, ws, uniqueId, cid)
		case <-quit:
			log.Println("connection end.")
			return
		}
	}

	ws.Close()
	delete(WebsockRepos, uniqueId)
}

func reportRuntimeInfo(ch chan int, ws *websocket.Conn, uniqueId string, cid string) {
	if ws == nil {
		log.Println("ws == nil")
		return
	}

	//log.Println("refresh:", cid)

	rconn := rhelper.Pool().Get()

	//runtimeInfo, _ := redis.Strings(rconn.Do("lrange",rhelper.PurifiersStatus + cid, "0", "0"))
	deviceStatus, _ := redis.Strings(rconn.Do("HMGET", rhelper.PurifiersStatusNow+cid, "powerState", "screenSleepState",
		"autoState", "moonState", "pwm", "speedLeve", "aqi", "preFanSpeed", "filterUseTime", "filterLife", "online"))
	//log.Println("lrange",rhelper.ControllerHash + cid, "0", "0")

	if len(deviceStatus) == 0 {
		return
	}

	type HomeDevice_dev_status_purifier struct {
		Uri              string  `json:"uri"`
		PowerState       string  `json:"powerState"`
		ScreenSleepState string  `json:"screenSleepState"`
		AutoState        string  `json:"autoState"`
		MoonState        string  `json:"moonState"`
		Pwm              int     `json:"pwm"`
		SpeedLeve        int     `json:"speedLeve"`
		Aqi              int     `json:"aqi"`
		FilterUseTime    int     `json:"filterUseTime"`
		FilterLife       float64 `json:"filterLife"`
		PreFanSpeed      int     `json:"preFanSpeed"`
		WorkMode         string  `json:"workMode"`
		Online           string  `json:"online"`
	}

	device := &HomeDevice_dev_status_purifier{}

	device.Online = rhelper.GetControllerOnlineStatus(config.ServerId, cid)
	device.PowerState = deviceStatus[0]
	device.ScreenSleepState = deviceStatus[1]
	device.AutoState = deviceStatus[2]
	device.MoonState = deviceStatus[3]
	device.Pwm, _ = strconv.Atoi(deviceStatus[4])
	device.SpeedLeve, _ = strconv.Atoi(deviceStatus[5])
	device.FilterLife, _ = strconv.ParseFloat(deviceStatus[9], 5)
	device.FilterUseTime, _ = strconv.Atoi(deviceStatus[8])
	device.Aqi, _ = strconv.Atoi(deviceStatus[6])
	if device.AutoState == "on" {
		device.WorkMode = "auto"
	} else if device.SpeedLeve == 4 {
		device.WorkMode = "sleep"
	} else {
		device.WorkMode = ""
	}

	buf, _ := json.Marshal(device)

	err := ws.WriteMessage(websocket.TextMessage, buf)
	rconn.Close()
	if err != nil {
		log.Println("write:", err)
		ch <- 0
		return
	}
}

type Message struct {
	Type     string `json:"type"`
	Id       int    `json:"id"`
	To       string `json:"to"`
	From     string `json:"from"`
	Title    string `json:"title"`
	Abstract string `json:"abstract"`
	Detail   string `json:"detail"`
	Stime    int    `json:"stime"`
}

func sendMessageToOldWS(account string) {
	msg := Message{}
	msg.Type = "logout"
	msg.To = account
	msg.From = "system"
	msg.Abstract = "Log exception"
	msg.Detail = "Account login in another phone"
	msg.Title = "Account login in another phone"

	SendMessage(msg)
}

func SendMessage(msg Message) error {
	rconn := rhelper.Pool().Get()
	defer rconn.Close()

	id, err := redis.Int(rconn.Do("INCR", rhelper.MsgIdHolder))
	if err != nil {
		log.Println("SendMessage err:", err)
		return err
	}

	msg.Id = id

	msg.Stime = int(time.Now().Unix())

	b, err := json.Marshal(msg)
	if err != nil {
		log.Println("SendMessage error:", err)
		return err
	}

	Send2WS(msg.To, b, msg.Title)
	return nil
}

// alert just use for ios
func Send2WS(account string, jsonMsg []byte, alert string) error {
	var wsHolder, ok = WebsockRepos[account]
	if !ok {
		//return fmt.Errorf("account not connect:%s", account)
		tk := loadApnsTkFromDB(account)
		if tk == "" {
			return fmt.Errorf("account not connect:%s", account)
		}

		send2APNS(jsonMsg, tk, alert)
		return nil
	}

	log.Printf("Send2WS, account %s, msg:%s", account, jsonMsg)

	devtk := wsHolder.cid
	if devtk != "" {
		log.Printf("send to ios")
		send2APNS(jsonMsg, devtk, alert)
		return nil
	} else {
		log.Printf("send to android")
		return wsHolder.ws.WriteMessage(websocket.TextMessage, jsonMsg)
	}
}

func send2APNS(msg []byte, devtk string, alert string) {
	if devtk == "" {
		return
	}

	payload := apns.NewPayload()
	payload.Alert = alert
	payload.Badge = 1
	payload.Sound = "bingbong.aiff"

	pn := apns.NewPushNotification()
	pn.DeviceToken = devtk
	pn.AddPayload(payload)
	pn.Set("message", string(msg))

	certPath := fmt.Sprintf("%s%s", config.CertificatesDirectory, "/cert.pem")
	keyPath := fmt.Sprintf("%s%s", config.CertificatesDirectory, "/key-noenc.pem")

	log.Printf("send2APNS, certPath %s, keyPath:%s, apnsurl:%s, devtk:%s", certPath, keyPath, config.APNSUrl, devtk)
	client := apns.NewClient(config.APNSUrl, certPath, keyPath)

	resp := client.Send(pn)
	myAlert, _ := pn.PayloadString()
	log.Printf("  Alert: %s", myAlert)
	log.Printf("Success: %s", resp.Success)
	log.Printf("  Error: %s", resp.Error)
}

func loadApnsTkFromDB(account string) (tk string) {
	rconn := rhelper.Pool().Get()
	defer rconn.Close()

	tk, err := redis.String(rconn.Do("HGET", rhelper.ApnsToken, account))
	if err != nil || tk == "" {
		log.Println("loadApnsTkFromDB failed, error:", err)
		return ""
	}

	return tk
}
